package com.ruoyi.common.utils.junit;

import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.core.domain.model.LoginBody;
import com.ruoyi.common.utils.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.mock.web.MockHttpServletResponse;
import org.springframework.mock.web.MockMultipartFile;
import org.springframework.stereotype.Component;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.MvcResult;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import org.springframework.test.web.servlet.result.MockMvcResultMatchers;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.context.WebApplicationContext;

import javax.annotation.PostConstruct;
import javax.servlet.Filter;
import java.io.File;
import java.io.FileInputStream;
import java.nio.charset.StandardCharsets;

import static org.hamcrest.CoreMatchers.equalTo;
import static org.junit.Assert.assertThat;

/**
 * Junit 测试工具类
 *
 * @author ByChen
 */
@Component
public class JunitUtils {
    private MockMvc mockMvc;
    @Autowired
    private WebApplicationContext wac;
    @Autowired
    private Filter springSecurityFilterChain;
    @Autowired
    private static JunitUtils junitUtils;

    @PostConstruct
    public void init() {
        junitUtils = this;
        junitUtils.mockMvc = this.mockMvc;
        junitUtils.springSecurityFilterChain = this.springSecurityFilterChain;
        junitUtils.wac = this.wac;
        this.mockMvc = MockMvcBuilders.webAppContextSetup(junitUtils.wac).addFilter(junitUtils.springSecurityFilterChain).build();
    }


    /**
     * 登录获取token
     *
     * @return
     */
    public static String login() {
        LoginBody loginBody = new LoginBody();
        loginBody.setUsername("admin");
        loginBody.setPassword("admin123");
        loginBody.setCode("0");
        loginBody.setUuid("f385382f6ca14e45ba8a4f242fdfb234");
        AjaxResult ajaxResult = jUnitPostWithOutToken("/login", JSONObject.toJSONString(loginBody), AjaxResult.class);
        return ajaxResult.get("token").toString();
    }

    /**
     * 学员登录获取sessionKey
     *
     * @return
     */
    public static String studentLogin() {
        String student = "{\n" +
                "    \"idCard\":\"410222195604147366\",\n" +
                "    \"password\":\"147366\",\n" +
                "    \"courseId\":\"27\"\n" +
                "}";
        AjaxResult ajaxResult = jUnitPostWithOutToken("/api/student/login", student, AjaxResult.class);
        Object data = ajaxResult.get("data");
        return JSON.parseObject(JSONObject.toJSONString(data)).get("sessionKey").toString();
    }


    /**
     * Junit 请求接口 Get请求
     *
     * @param url     请求地址 支持拼接参数 例如 /system/user/list?pageNum=1&pageSize=10
     * @param tClass  接口返回类class
     * @param token   接口鉴权token
     * @param session 接口权限session
     * @return
     */
    public static <T> T jUnitGet(String url, Class<T> tClass, String token, String session) {
        HttpHeaders httpHeaders = new HttpHeaders();
        httpHeaders.setBearerAuth(token);
        if (StringUtils.isNotBlank(session)) {
            httpHeaders.set("session-key", session);
        }
        //get请求
        try {
            MvcResult mvcResult = junitUtils.mockMvc
                    .perform(MockMvcRequestBuilders
                            .get(url)
                            .headers(httpHeaders)
                            .contentType(MediaType.APPLICATION_JSON))
                    .andExpect(MockMvcResultMatchers.status().isOk())
                    .andReturn();
            MockHttpServletResponse response = mvcResult.getResponse();
            String bodyString = response.getContentAsString(StandardCharsets.UTF_8);
            //断言判断返回结果
            assertCustom(JSONObject.parse(bodyString), 200);
            return JSONObject.parseObject(bodyString, tClass);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * Junit 请求接口 POST请求
     *
     * @param url       请求地址  例如 /login
     * @param paramJson 接口参数json串 例如 "{\n" +
     *                  "  \"username\": \"admin\",\n" +
     *                  "  \"password\": \"admin123\",\n" +
     *                  "  \"code\": 0,\n" +
     *                  "  \"uuid\": \"f385382f6ca14e45ba8a4f242fdfb234\"\n" +
     *                  "}"
     * @param tClass    接口返回类class
     * @param token     接口鉴权token
     * @param session   接口权限session
     * @return
     */
    public static <T> T jUnitPost(String url, String paramJson, Class<T> tClass, String token, String session) {
        try {
            HttpHeaders httpHeaders = new HttpHeaders();
            httpHeaders.setBearerAuth(token);
            if (StringUtils.isNotBlank(session)) {
                httpHeaders.set("session-key", session);
            }
            //post请求
            MvcResult mvcResult = junitUtils.mockMvc.perform(MockMvcRequestBuilders
                    .post(url)
                    .headers(httpHeaders)
                    .contentType(MediaType.APPLICATION_JSON)
                    .content(paramJson)
                    .accept(MediaType.APPLICATION_JSON))
                    .andReturn();
            MockHttpServletResponse response = mvcResult.getResponse();
            String bodyString = response.getContentAsString(StandardCharsets.UTF_8);
            //断言判断返回结果
            assertCustom(JSONObject.parse(bodyString), 200);
            return JSONObject.parseObject(bodyString, tClass);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * Junit 请求接口 PUT请求
     *
     * @param url       请求地址  例如 /login
     * @param paramJson 接口参数json串 例如 "{\n" +
     *                  "  \"username\": \"admin\",\n" +
     *                  "  \"password\": \"admin123\",\n" +
     *                  "  \"code\": 0,\n" +
     *                  "  \"uuid\": \"f385382f6ca14e45ba8a4f242fdfb234\"\n" +
     *                  "}"
     * @param tClass    接口返回类class
     * @param token     接口鉴权token
     * @param session   接口权限session
     * @return
     */
    public static <T> T jUnitPut(String url, String paramJson, Class<T> tClass, String token, String session) {
        try {
            HttpHeaders httpHeaders = new HttpHeaders();
            httpHeaders.setBearerAuth(token);
            if (StringUtils.isNotBlank(session)) {
                httpHeaders.set("session-key", session);
            }
            //post请求
            MvcResult mvcResult = junitUtils.mockMvc.perform(MockMvcRequestBuilders
                    .put(url)
                    .headers(httpHeaders)
                    .contentType(MediaType.APPLICATION_JSON)
                    .content(paramJson)
                    .accept(MediaType.APPLICATION_JSON))
                    .andReturn();
            MockHttpServletResponse response = mvcResult.getResponse();
            String bodyString = response.getContentAsString(StandardCharsets.UTF_8);
            //断言判断返回结果
            assertCustom(JSONObject.parse(bodyString), 200);
            return JSONObject.parseObject(bodyString, tClass);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * Junit 请求接口 DELETE请求
     *
     * @param url       请求地址  例如 /login
     * @param paramJson 接口参数json串 例如 "{\n" +
     *                  "  \"username\": \"admin\",\n" +
     *                  "  \"password\": \"admin123\",\n" +
     *                  "  \"code\": 0,\n" +
     *                  "  \"uuid\": \"f385382f6ca14e45ba8a4f242fdfb234\"\n" +
     *                  "}"
     * @param tClass    接口返回类class
     * @param token     接口鉴权token
     * @param session   接口权限session
     * @return
     */
    public static <T> T jUnitDelete(String url, String paramJson, Class<T> tClass, String token, String session) {
        try {
            HttpHeaders httpHeaders = new HttpHeaders();
            httpHeaders.setBearerAuth(token);
            if (StringUtils.isNotBlank(session)) {
                httpHeaders.set("session-key", session);
            }
            //post请求
            MvcResult mvcResult = junitUtils.mockMvc.perform(MockMvcRequestBuilders
                    .delete(url)
                    .headers(httpHeaders)
                    .contentType(MediaType.APPLICATION_JSON)
                    .content(paramJson!=null?paramJson:"{}")
                    .accept(MediaType.APPLICATION_JSON))
                    .andReturn();
            MockHttpServletResponse response = mvcResult.getResponse();
            String bodyString = response.getContentAsString(StandardCharsets.UTF_8);
            //断言判断返回结果
            assertCustom(JSONObject.parse(bodyString), 200);
            return JSONObject.parseObject(bodyString, tClass);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 不去获取token的请求
     *
     * @param url       请求地址  例如 /login
     * @param paramJson 接口参数json串
     * @param tClass    接口返回类class
     * @param <T>       泛型
     * @return
     */
    public static <T> T jUnitPostWithOutToken(String url, String paramJson, Class<T> tClass) {
        try {
            //post请求
            MvcResult mvcResult = junitUtils.mockMvc.perform(MockMvcRequestBuilders
                    .post(url)
                    .contentType(MediaType.APPLICATION_JSON)
                    .content(paramJson)
                    .accept(MediaType.APPLICATION_JSON))
                    .andReturn();
            MockHttpServletResponse response = mvcResult.getResponse();
            String bodyString = response.getContentAsString(StandardCharsets.UTF_8);
            //断言判断返回结果
            assertCustom(JSONObject.parse(bodyString), 200);
            return JSONObject.parseObject(bodyString, tClass);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 上传文件
     *
     * @param url      接口路径
     * @param fileUrl  文件路径
     * @param fileName 文件名
     * @param tClass   接口返回类class
     * @param token    token
     * @param <T>      泛型
     * @return
     */
    public static <T> T jUnitFileUpload(String url, String fileUrl, String fileName, Class<T> tClass, String token) {
        try {
            File file = new File(fileUrl);
            MockMultipartFile firstFile = new MockMultipartFile("file", fileName,
                    MediaType.TEXT_PLAIN_VALUE, new FileInputStream(file));
            HttpHeaders httpHeaders = new HttpHeaders();
            httpHeaders.setBearerAuth(token);
            //post请求
            MvcResult mvcResult = junitUtils.mockMvc.perform(MockMvcRequestBuilders
                    .multipart(url)
                    .file(firstFile)
                    .headers(httpHeaders)
                    .contentType(MediaType.MULTIPART_FORM_DATA_VALUE))
                    .andReturn();
            MockHttpServletResponse response = mvcResult.getResponse();
            String bodyString = response.getContentAsString(StandardCharsets.UTF_8);
            //断言判断返回结果
            assertCustom(JSONObject.parse(bodyString), 200);
            return JSONObject.parseObject(bodyString, tClass);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }


    /**
     * 断言，判断返回结果
     *
     * @param bodyJson 返回体
     * @param code     正确的code 200
     */
    public static void assertCustom(JSONObject bodyJson, int code) {
        assertThat(bodyJson.get("code"), equalTo(code));
        if (bodyJson.toString().contains("\"msg\":\"数据库异常，请联系管理员")) {
            throw new AssertionError("数据库异常，请联系管理员");
        }
        if (bodyJson.toString().contains("\"msg\":\"没有权限，请联系管理员授权")) {
            throw new AssertionError("没有权限，请联系管理员授权");
        }
        if (bodyJson.toString().contains("\"msg\":\"数据为空")) {
            throw new AssertionError("数据为空");
        }
        if (bodyJson.toString().contains("\"msg\":\"演示模式，不允许操作")) {
            throw new AssertionError("演示模式，不允许操作");
        }
    }
}
